#######################################################
# The class of clock-based DEVS
# Author: moonho.hwang@gmail.com
#   Date: Oct.19.2011
#######################################################
from Simulatable import *
from XSY.Set.RandomVariables import *
from XSY.Base import isnumeric

class CDEVS(Simulatable):
   '''
The class for Clock-based DEVS (CDEVS). 

It has the clock set <i> C={(c=(tL,tN)} </i> which needs to be initialized by tL=0 and tN ~ init_tN[c] when calling the initilize function delta_0(). 

Each clock <i>c=(tL, tN)</i> where <i>tL</i> and <i>tN</i> denote the last event time and the next event tim, respectively. We can convert the clock <i>c=(tL,tN)</i> to <i>(sigma, e)</i> where <i>sigma</i> and <i>e</i> together with the current time <i>t</i>: <i>sigma = tN - t </i> and <i>e=t - tL</i>.
   '''

   def __init__(self, name, init_tN, parent=None):
      '''constructor'''
      Simulatable.__init__(self, name, parent)
      if isinstance(init_tN, dict):
         self.init_tN=init_tN # initial status of {(name, clock)}
         self.C = {}
         #self.delta_0()
         self.IMMC = []   # clock names of immediate clocks 
      else:
         raise Exception("init_tN is not dictionary but %s"%classname(init_tN))

   def delta_0(self):
      '''reset clocks '''
      #self.C = dict();  # reset
      self.tL = 0; self.tN = float('inf')
      for k in self.init_tN.keys():
         tn = self.init_tN[k]
         if isnumeric(tn): v = tn
         elif isinstance(tn, RandomVariable): v = tn.sample()
         else: raise Exception("Invalide clock tn value in CDEVS")
         self.C[k]=Clock(0, v)


   def update_IMMC(self):
      '''compute remaining time at time t'''
      tNext = float('inf')
      self.IMMC = []
      for k in self.C.keys(): 
         clock = self.C[k]
         if clock.tN <= tNext:
            if clock.tN < tNext:
               self.IMMC = [] 
            tNext = clock.tN
            self.IMMC.append(k)
         
   def ta(self):
      '''return tr(Clock.tNow)'''
      self.update_IMMC()

      if len(self.IMMC) > 0: return self.C[self.IMMC[0]].tN -Clock.tNow
      else:                  return float('inf')

   def state_to_s(self):

      s = "C={"
      Ckeys = self.C.keys()
      Ckeys.sort()
      first=True
      for k in Ckeys:
         if first == False: s += ","
         first = False
         s += str(k)+":"+str(self.C[k])
      s += "}"
      return s

   def __str__(self):
      s = Simulatable.__str__(self)
      s += "init_tN={"
      Ckeys = self.init_tN.keys()
      Ckeys.sort()
      first=True
      for k in Ckeys:
         if first == False: s += ","
         first = False
         s += str(k)+":"+str(self.init_tN[k])
      s += "}"

      s += ", " + self.state_to_s()
      return s

if __name__ == '__main__':
   cdevs = CDEVS("cdevs_eg",{'c1':10})
   print cdevs
   print "state_to_s=%s"%cdevs.state_to_s()

   print "ta()=", cdevs.ta()
